﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GcmpApiFoundation.h"
#include "StringUtil.h"
#include <unordered_map>

namespace gdmp
{
    namespace api
    {
        class Object;

        // 用于管理Api类型映射
        class ApiTypeMap final
        {
        public:
            GCMP_API_FOUNDATION_EXPORT static ApiTypeMap& Get() noexcept;

            // 判断输入的type是否是类型T的派生类
            template<typename T, std::enable_if_t<std::is_base_of<Object, T>::value, int> = 0>
            bool IsSubclassOf(const std::wstring& type) noexcept
            {
                auto& iter = m_typeMapping.find(type);
                std::wstring apiType = iter != m_typeMapping.end() ? iter->second : type;
                const std::wstring& baseType = ::gcmp::StringUtil::ToWString(T::ClassType());
                if (apiType == baseType)
                    return false;

                return IsSubclassOf(apiType, baseType);
            }

            ///根据类型名判断前者是否是后者的基类(效果等同于std::is_base_of<Ty1,Ty2>)
            bool IsBaseOf(const std::wstring& baseType, const std::wstring& derivedType) noexcept
            {
                auto& iter = m_typeMapping.find(baseType);
                std::wstring apiType = iter != m_typeMapping.end() ? iter->second : baseType;
                if (apiType == derivedType)
                    return true;

                return IsSubclassOf(derivedType, apiType);
            }

        private:
            bool IsSubclassOf(const std::wstring& derivedType, const std::wstring& baseType) noexcept
            {
                auto iter = m_typeRelationMap.find(derivedType);
                if (iter == m_typeRelationMap.end())
                {
                    return false;
                }

                if (iter->second == baseType)
                    return true;

                return IsSubclassOf(iter->second, baseType);
            }

            template<typename T, typename BT>
            void Register() noexcept
            {
                const std::wstring& type = ::gcmp::StringUtil::ToWString(T::ClassType());
                if (m_typeRelationMap.find(type) == m_typeRelationMap.end())
                {
                    m_typeRelationMap.emplace(type, ::gcmp::StringUtil::ToWString(BT::ClassType()));
                }
            }

            void Register(const wchar_t* apiTypeName, const wchar_t* managedTypeName)
            {
                if (m_typeMapping.find(apiTypeName) == m_typeMapping.end()) {
                    m_typeMapping.emplace(managedTypeName, apiTypeName);
                }
            }

            std::unordered_map<std::wstring, std::wstring> m_typeRelationMap;
            std::unordered_map<std::wstring, std::wstring> m_typeMapping;

            template<typename T, typename BT, std::enable_if_t<std::is_base_of<BT, T>::value, int>>
            friend class ApiTypemapHelper;
            friend class ApiTypeMapping;
        };

        template<typename T, typename BT, std::enable_if_t<std::is_base_of<BT, T>::value, int> = 0>
        class ApiTypemapHelper final
        {
        public:
            ApiTypemapHelper()
            {
                gdmp::api::ApiTypeMap::Get().Register<T, BT>();
            }
        };

        class ApiTypeMapping final
        {
        public:
            ApiTypeMapping(const wchar_t* apiTypeName, const wchar_t* managedTypeName)
            {
                ApiTypeMap::Get().Register(apiTypeName, managedTypeName);
            }
        };
    }
}

#define REGISTER_API_TYPE(APITYPE, BASE_TYPE) \
        namespace{ \
            gdmp::api::ApiTypemapHelper<APITYPE, BASE_TYPE> s_typemapHelper; \
        }

#define REGISTER_API_TYPE_MAPPING(APITYPE_NAME, MANAGED_TYPE_NAME) \
        namespace{ \
            gdmp::api::ApiTypeMapping s_typeMapping(APITYPE_NAME, MANAGED_TYPE_NAME); \
        }
